// $Id: proxy_ptr_array.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <algorithm>
#include <cstring>
#include <boost/test/unit_test.hpp>
#include <xpcom/ptr_array.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/proxy/array.hpp>
#include "interface_def.hpp"

namespace xpcom {
namespace {

void do_in_impl_1(IBase** a, uint32_t s, uint32_t l)
{
    BOOST_CHECK(a == 0);
    BOOST_CHECK(s == 0);
    BOOST_CHECK(l == 0);
}

void do_in2_impl_1(IBase** a, uint32_t s)
{
    BOOST_CHECK(a == 0);
    BOOST_CHECK(s == 0);
}

void do_in_1(const ptr_array<IBase>& a)
{
    do_in_impl_1(proxy::array_in(a), proxy::array_in_size(a), proxy::array_in_length(a));
}

void do_in2_1(const ptr_array<IBase>& a)
{
    do_in2_impl_1(proxy::array_in(a), proxy::array_in_size2(a));
}

void do_in_impl_2(IBase** a, uint32_t s, uint32_t l)
{
    BOOST_CHECK(a != 0);
    BOOST_CHECK(l == 5);
    BOOST_CHECK(s >= l);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL(a[i]->getN(), i);
    }
}

void do_in2_impl_2(IBase** a, uint32_t s)
{
    BOOST_CHECK(a != 0);
    BOOST_CHECK(s == 5);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL(a[i]->getN(), i);
    }
}

void do_in_2(const ptr_array<IBase>& a)
{
    do_in_impl_2(proxy::array_in(a), proxy::array_in_size(a), proxy::array_in_length(a));
}

void do_in2_2(const ptr_array<IBase>& a)
{
    do_in2_impl_2(proxy::array_in(a), proxy::array_in_size2(a));
}

void do_in2_impl_3(IBase** a, IBase** b, IBase** c, uint32_t s)
{
    BOOST_CHECK(a != 0 && b != 0 && c != 0);
    BOOST_CHECK(s == 5);
}

void do_in2_3(const ptr_array<IBase>& a, const ptr_array<IBase>& b, const ptr_array<IBase>& c)
{
    do_in2_impl_3(proxy::array_in(a), proxy::array_in(b), proxy::array_in(c),
            proxy::array_in_size2(a, b, c));
}

void do_out_impl_1(IBase*** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = 0;
    *s = 0;
    *l = 0;
}

void do_out2_impl_1(IBase*** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = 0;
    *s = 0;
}

void do_out_1(ptr_array<IBase>& a)
{
    do_out_impl_1(proxy::array_out(a), proxy::array_out_size(a), proxy::array_out_length(a));
}

void do_out2_1(ptr_array<IBase>& a)
{
    do_out2_impl_1(proxy::array_out(a), proxy::array_out_size2(a));
}

void do_out_impl_2(IBase*** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<IBase*>(10);
    for (int i = 0; i < 6; ++i) {
        ref_ptr<IBase> p(X_make_instance());
        p->setN(i);
        (*a)[i] = p.release();
    }
    *s = 10;
    *l = 6;
}

void do_out2_impl_2(IBase*** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<IBase*>(10);
    for (int i = 0; i < 6; ++i) {
        ref_ptr<IBase> p(X_make_instance());
        p->setN(i);
        (*a)[i] = p.release();
    }
    *s = 6;
}

void do_out_2(ptr_array<IBase>& a)
{
    do_out_impl_2(proxy::array_out(a), proxy::array_out_size(a), proxy::array_out_length(a));
}

void do_out2_2(ptr_array<IBase>& a)
{
    do_out2_impl_2(proxy::array_out(a), proxy::array_out_size2(a));
}

void do_out_impl_3(IBase*** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<IBase*>(10);
    *s = 10;
    *l = 0;
}

void do_out2_impl_3(IBase*** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<IBase*>(10);
    *s = 0;
}

void do_out_3(ptr_array<IBase>& a)
{
    do_out_impl_3(proxy::array_out(a), proxy::array_out_size(a), proxy::array_out_length(a));
}

void do_out2_3(ptr_array<IBase>& a)
{
    do_out2_impl_3(proxy::array_out(a), proxy::array_out_size2(a));
}

void do_inout_impl_1(IBase*** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<IBase*>(10);
    for (int i = 0; i < 4; ++i) {
        ref_ptr<IBase> p(X_make_instance());
        p->setN(i * 10);
        (*a)[i] = p.release();
    }
    *s = 10;
    *l = 4;
}

void do_inout2_impl_1(IBase*** a, uint32_t* s)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<IBase*>(10);
    for (int i = 0; i < 4; ++i) {
        ref_ptr<IBase> p(X_make_instance());
        p->setN(i * 10);
        (*a)[i] = p.release();
    }
    *s = 4;
}

void do_inout_1(ptr_array<IBase>& a)
{
    do_inout_impl_1(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_1(ptr_array<IBase>& a)
{
    do_inout2_impl_1(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_inout_impl_2(IBase*** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*l == 5);
    BOOST_CHECK(*s >= *l);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i]->getN(), i * 10);
    }

    std::reverse((*a), (*a) + *l);
    (*a)[4]->release();
    *l = 4;
}

void do_inout2_impl_2(IBase*** a, uint32_t* s)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*s == 5);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i]->getN(), i * 10);
    }

    std::reverse((*a), (*a) + *s);
    (*a)[4]->release();
    *s = 4;
}

void do_inout_2(ptr_array<IBase>& a)
{
    do_inout_impl_2(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_2(ptr_array<IBase>& a)
{
    do_inout2_impl_2(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_inout_impl_3(IBase*** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*l == 5);
    BOOST_CHECK(*s >= *l);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i]->getN(), i * 10);
    }

    for (int i = 0; i < 5; ++i) {
        (*a)[i]->release();
    }

    memory::free(*a);
    *s = *l = 0;
    *a = memory::alloc<IBase*>(20);
    for (int i = 0; i < 18; ++i) {
        ref_ptr<IBase> p(X_make_instance());
        p->setN(i * 100);
        (*a)[i] = p.release();
    }
    *s = 20;
    *l = 18;
}

void do_inout2_impl_3(IBase*** a, uint32_t* s)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*s == 5);
    for (int i = 0; i < 5; ++i) {
        BOOST_CHECK_EQUAL((*a)[i]->getN(), i * 10);
    }

    for (int i = 0; i < 5; ++i) {
        (*a)[i]->release();
    }

    memory::free(*a);
    *s = 0;
    *a = memory::alloc<IBase*>(20);
    for (int i = 0; i < 18; ++i) {
        ref_ptr<IBase> p(X_make_instance());
        p->setN(i * 100);
        (*a)[i] = p.release();
    }
    *s = 18;
}

void do_inout_3(ptr_array<IBase>& a)
{
    do_inout_impl_3(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_3(ptr_array<IBase>& a)
{
    do_inout2_impl_3(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_inout_impl_4(IBase*** a, uint32_t* s, uint32_t* l)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*l == 10);
    BOOST_CHECK(*s >= *l);
    for (int i = 0; i < 10; ++i) {
        BOOST_CHECK_EQUAL((*a)[i]->getN(), i * 100);
    }

    for (int i = 0; i < 10; ++i) {
        (*a)[i]->release();
    }

    memory::free(*a);
    *a = 0;
    *s = 0;
    *l = 0;
}

void do_inout2_impl_4(IBase*** a, uint32_t* s)
{
    BOOST_CHECK(*a != 0);
    BOOST_CHECK(*s == 10);
    for (int i = 0; i < 10; ++i) {
        BOOST_CHECK_EQUAL((*a)[i]->getN(), i * 100);
    }

    for (int i = 0; i < 10; ++i) {
        (*a)[i]->release();
    }

    memory::free(*a);
    *a = 0;
    *s = 0;
}

void do_inout_4(ptr_array<IBase>& a)
{
    do_inout_impl_4(proxy::array_inout(a), proxy::array_inout_size(a), proxy::array_inout_length(a));
}

void do_inout2_4(ptr_array<IBase>& a)
{
    do_inout2_impl_4(proxy::array_inout(a), proxy::array_inout_size2(a));
}

void do_out_isize2_impl_1(IBase*** a, uint32_t n)
{
    BOOST_CHECK(*a == 0);
    *a = memory::alloc<IBase*>(n);

    ref_ptr<IBase> p(X_make_instance());
    p->setN(8);

    for (int i = 0; i < (int)n; ++i) {
        ref_ptr<IBase> p2(p);
        (*a)[i] = p2.release();
    }
}

void do_out_isize2_1(ptr_array<IBase>& a, uint32_t n)
{
    do_out_isize2_impl_1(proxy::array_out_isize2(a, n), n);
}

void do_out_isize2_impl_2(IBase*** a, uint32_t n)
{
    BOOST_CHECK(*a == 0);
    *a = 0;
}

void do_out_isize2_2(ptr_array<IBase>& a, uint32_t n)
{
    do_out_isize2_impl_2(proxy::array_out_isize2(a, n), n);
}

}
}

BOOST_AUTO_TEST_CASE(test_proxy_ptr_array_in)
{
    using namespace xpcom;

    X_count_guard g;
    X_count(0);
    {
        ptr_array<IBase> a1;
        ptr_array<IBase> a2;

        a2.reserve(10);
        a2.resize(5);
        for (int i = 0; i < 5; ++i) {
            a2[i].reset(X_make_instance());
            a2[i]->setN(i);
        }

        do_in_1(a1);
        do_in_2(a2);
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE(test_proxy_ptr_array_in2)
{
    using namespace xpcom;

    X_count_guard g;
    X_count(0);
    {
        ptr_array<IBase> a1;
        ptr_array<IBase> a2;
        ptr_array<IBase> a3(20);

        a2.reserve(10);
        a2.resize(5);
        for (int i = 0; i < 5; ++i) {
            a2[i].reset(X_make_instance());
            a2[i]->setN(i);
        }

        do_in2_1(a1);
        do_in2_2(a2);

        a1.resize(5);
        a2.resize(50);
        do_in2_3(a1, a2, a3);
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE(test_proxy_ptr_array_out)
{
    using namespace xpcom;

    X_count_guard g;
    X_count(0);
    {
        ptr_array<IBase> a(9);

        do_out_1(a);
        BOOST_CHECK(a.empty());

        do_out_2(a);
        BOOST_CHECK(a.size() == 6);
#ifdef XPCOM_ENABLE_LENGTH_IS
        BOOST_CHECK(a.capacity() == 10);
#endif
        for (int i = 0; i < 6; ++i) {
            BOOST_CHECK_EQUAL(a[i]->getN(), i);
        }

        do_out_3(a);
        BOOST_CHECK(a.empty());
#ifdef XPCOM_ENABLE_LENGTH_IS
        BOOST_CHECK(a.capacity() == 10);
#endif
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE(test_proxy_ptr_array_out2)
{
    using namespace xpcom;

    X_count_guard g;
    X_count(0);
    {
        ptr_array<IBase> a(9);

        do_out2_1(a);
        BOOST_CHECK(a.empty());

        do_out2_2(a);
        BOOST_CHECK(a.size() == 6);
        for (int i = 0; i < 6; ++i) {
            BOOST_CHECK_EQUAL(a[i]->getN(), i);
        }

        do_out2_3(a);
        BOOST_CHECK(a.empty());
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE(test_proxy_ptr_array_inout)
{
    using namespace xpcom;

    X_count_guard g;
    X_count(0);
    {
        ptr_array<IBase> a;

        do_inout_1(a);
        BOOST_CHECK(a.size() == 4);
#ifdef XPCOM_ENABLE_LENGTH_IS
        BOOST_CHECK(a.capacity() == 10);
#endif
        for (int i = 0; i < 4; ++i) {
            BOOST_CHECK_EQUAL(a[i]->getN(), i * 10);
        }

        a.resize(5);
        a[4].reset(X_make_instance());
        a[4]->setN(40);
        do_inout_2(a);
        BOOST_CHECK(a.size() == 4);
        BOOST_CHECK_EQUAL(a[0]->getN(), 40);
        BOOST_CHECK_EQUAL(a[1]->getN(), 30);
        BOOST_CHECK_EQUAL(a[2]->getN(), 20);
        BOOST_CHECK_EQUAL(a[3]->getN(), 10);

        a.resize(5);
        for (int i = 0; i < 5; ++i) {
            a[i].reset(X_make_instance());
            a[i]->setN(i * 10);
        }
        do_inout_3(a);
        BOOST_CHECK(a.size() == 18);
#ifdef XPCOM_ENABLE_LENGTH_IS
        BOOST_CHECK(a.capacity() == 20);
#endif
        for (int i = 0; i < 18; ++i) {
            BOOST_CHECK_EQUAL(a[i]->getN(), i * 100);
        }

        a.resize(10);
        do_inout_4(a);
        BOOST_CHECK(a.empty());
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE(test_proxy_ptr_array_inout2)
{
    using namespace xpcom;

    X_count_guard g;
    X_count(0);
    {
        ptr_array<IBase> a;

        do_inout2_1(a);
        BOOST_CHECK(a.size() == 4);
        for (int i = 0; i < 4; ++i) {
            BOOST_CHECK_EQUAL(a[i]->getN(), i * 10);
        }

        a.resize(5);
        a[4].reset(X_make_instance());
        a[4]->setN(40);
        do_inout2_2(a);
        BOOST_CHECK(a.size() == 4);
        BOOST_CHECK_EQUAL(a[0]->getN(), 40);
        BOOST_CHECK_EQUAL(a[1]->getN(), 30);
        BOOST_CHECK_EQUAL(a[2]->getN(), 20);
        BOOST_CHECK_EQUAL(a[3]->getN(), 10);

        a.resize(5);
        for (int i = 0; i < 5; ++i) {
            a[i].reset(X_make_instance());
            a[i]->setN(i * 10);
        }
        do_inout2_3(a);
        BOOST_CHECK(a.size() == 18);
        for (int i = 0; i < 18; ++i) {
            BOOST_CHECK_EQUAL(a[i]->getN(), i * 100);
        }

        a.resize(10);
        do_inout2_4(a);
        BOOST_CHECK(a.empty());
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

BOOST_AUTO_TEST_CASE(test_proxy_ptr_array_out_isize2)
{
    using namespace xpcom;

    X_count_guard g;
    X_count(0);
    {
        ptr_array<IBase> a(1);

        do_out_isize2_1(a, 8);
        BOOST_CHECK(a.size() == 8);
        for (int i = 0; i < 8; ++i) {
            BOOST_CHECK_EQUAL(a[i]->getN(), 8);
        }

        do_out_isize2_1(a, 0);
        BOOST_CHECK(a.empty());

        do_out_isize2_2(a, 5);
        BOOST_CHECK(a.empty());

        do_out_isize2_2(a, 0);
        BOOST_CHECK(a.empty());
    }
    BOOST_CHECK_EQUAL(X_count(), 0);
}

